﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Word = Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Word;
using Microsoft.Office.Tools.Word.Extensions;
using biolit_config;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace UCSD_BioLit
{    
    internal class WordCommonFuncs
    {
        /// <summary>
        /// title of ontology markup.
        /// </summary>
        private const string OntologyTitle = "named-content";

        /// <summary>
        /// ontology property name.
        /// </summary>
        private const string OntPropertyName = "content-type";

        /// <summary>
        /// Ontology attribute value.
        /// </summary>
        private const string OntPropertyValue = "biolit";

        /// <summary>
        /// Ontology id property name.
        /// </summary>
        private const string OntPropertyIdName = "id";

        /// <summary>
        /// ontology url name.
        /// </summary>
        private const string OntURLName = @"http://purl.org/obo/owl/";

        /// <summary>
        /// Ontology name property.
        /// </summary>
        private const string OntLabel = "OntName";

        /// <summary>
        /// DB markup title.
        /// </summary>
        private const string DBTitle = "ext-link";

        /// <summary>
        /// DB markup attrubute name.
        /// </summary>
        private const string DBAttributeName = "xlinkhref";

        /// <summary>
        /// DB markup first url.
        /// </summary>
        private const string DBURLFirstPart = @"http://www.rcsb.org/";

        /// <summary>
        /// DB markup second part.
        /// </summary>
        private const string DBURLSecPart = @"/explore/explore.do?structureId=";

        /// <summary>
        /// attribute name.
        /// </summary>
        private const string DBAttributeLabel = "ext-link-type";

        /// <summary>
        /// Title of the exception.
        /// </summary>
        private const string ExceptionTitle = "OntologyUtil";

        /// <summary>
        /// Used to check if the control is created in the particular namespace.
        /// </summary>
        /// <param name="controlid">control ID</param>
        /// <param name="controlnamespace">custom namespace</param>
        /// <returns>returns true/false based on the presence of control with the required namespace.</returns>
        public static bool CheckControlNamespace(string controlid, string controlnamespace)
        {
            if (controlid != null && !string.IsNullOrEmpty(controlnamespace))
            {
                Guid guidID = WordCommonFuncs.ConvertToGuid(controlid);

                if (guidID != null && guidID != Guid.Empty && ControlsStorage.ControlCollection.ContainsKey(guidID))
                {
                    ControlProperties prop = ControlsStorage.ControlCollection[guidID];
                    if (prop.ControlNamespace == controlnamespace)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Used to convert the id to a Guid.
        /// </summary>
        /// <param name="uniqueId">The id to be converted.</param>
        /// <returns>The Guid if the given Id is valid. Else, returns an empty Guid.</returns>
        public static Guid ConvertToGuid(string uniqueId)
        {
            Guid guid = Guid.Empty;

            if (!string.IsNullOrEmpty(uniqueId))
            {
                try
                {
                    guid = new Guid(uniqueId);
                }
                catch (FormatException formatEx)
                {
                    Debug.WriteLine(formatEx.Message, ExceptionTitle);
                }
                catch (OverflowException overflowEx)
                {
                    Debug.WriteLine(overflowEx.Message, ExceptionTitle);
                }
            }

            return guid;
        }
        
        /// <summary>
        /// Add Markup
        /// </summary>
        /// <param name="rangeVal">Word.Range rangeVal</param>
        /// <param name="NCBOID">string NCBI ID</param>
        /// <param name="IDValue">string IDValue</param>
        /// <param name="typeValue">string typeValue</param>
        /// <param name="OntologyName">ontology name</param>
        public static void OntologyMarkup(Word.Range rangeVal, string NCBOID, string IDValue, string typeValue, string OntologyName)
        {
            try
            {
                if (!string.IsNullOrEmpty(rangeVal.Text))
                {
                    object rangeRef = rangeVal;

                    // to check if any previous ontology control is present in the text range.
                    if (rangeVal.ContentControls.Count > 0)
                    {
                        foreach (Word.ContentControl node in rangeVal.ContentControls)
                        {
                            if (CheckControlNamespace(node.Tag, ThisAddIn.ONTOLOGY_NS_URI))
                            {
                                return;
                            }
                        }                       
                    }

                        // for parent controls.
                    else if (rangeVal.ParentContentControl != null)
                    {
                        if (CheckControlNamespace(rangeVal.ParentContentControl.Tag, ThisAddIn.ONTOLOGY_NS_URI))
                        {
                            return;
                        }
                    }

                    string[] idStringParts = IDValue.Split(new Char[] { ':' });
                    string urlName = string.Empty;
                    if (idStringParts.Length == 2)
                    {
                        urlName = OntURLName + idStringParts[0] + "#" + idStringParts[0] + "_" + idStringParts[1];
                    }

                    string buildString = "ncbo_id=" + Convert.ToString(NCBOID) + ";" + "term_id=" + Convert.ToString(IDValue) + ";term=" + Convert.ToString(rangeVal.Text) + ";url=" + urlName + "";
                    
                    // plain contentcontrol is added to the given text range.
                    Word.ContentControl CustomControl = rangeVal.ContentControls.Add(Word.WdContentControlType.wdContentControlRichText,
                       ref rangeRef);
                    
                    // name of the control is set to its title.
                    CustomControl.Title = OntologyTitle;
                    Guid tagID = Guid.NewGuid();
                    CustomControl.Tag = tagID.ToString();

                    CustomControl.SetPlaceholderText(null, null, buildString);
                    
                    // list to store the attributes of the control.
                    Dictionary<string, string> list = new Dictionary<string, string>();
                    list.Add(OntPropertyName, OntPropertyValue);
                    list.Add(OntPropertyIdName, buildString);
                    list.Add(OntLabel, OntologyName);
                    ControlProperties customProperties = new ControlProperties(ThisAddIn.ONTOLOGY_NS_URI,
                                                                             tagID.ToString(), list);
                    
                    // add the new control id and its control properties to the collection. 
                    ControlsStorage.ControlCollection.Add(tagID, customProperties);
                    
                    // to highlight the newly created markup.
                    BioLitRibbon br = (BioLitRibbon)Globals.Ribbons[0];
                    if (br.tbHighlight.Checked)
                    {
                        rangeVal.HighlightColorIndex = Microsoft.Office.Interop.Word.WdColorIndex.wdYellow;
                    }

                    // set the range
                    rangeVal.SetRange(CustomControl.Range.End + 1, CustomControl.Range.End + 1);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(Properties.Resources.ONTOLOGY_MARKUP_ERROR, ex);
            }
        }

        /// <summary>
        /// Method used to add custom tag markups.
        /// </summary>
        /// <param name="rangeVal">word range</param>
        /// <param name="IDValue">NCBI/PDB Id value</param>
        /// <param name="typeValue">type value</param>
        /// <param name="OntologyName">ontology name</param>
        public static void DBMarkup(Word.Range rangeVal, string IDValue, string typeValue, string OntologyName)
        {
            try
            {
                if (!string.IsNullOrEmpty(rangeVal.Text))
                {
                    object rangeRef = rangeVal;
                    
                    // to check if any previous ontology control is present in the text range.
                    if (rangeVal.ContentControls.Count > 0)
                    {
                        foreach (Word.ContentControl node in rangeVal.ContentControls)
                        {
                            if (CheckControlNamespace(node.Tag, ThisAddIn.DB_NS_URI))
                            { 
                                return; 
                            }
                        }
                    }

                    // for parent controls.
                    else if (rangeVal.ParentContentControl != null)
                    {
                        if (CheckControlNamespace(rangeVal.ParentContentControl.Tag, ThisAddIn.DB_NS_URI))
                        {
                            return;
                        }
                    }

                    // plain contentcontrol is added to the given text range.
                    Word.ContentControl dbControl = rangeVal.ContentControls.Add(Word.WdContentControlType.wdContentControlRichText,
                       ref rangeRef);
                    
                    // name of the control is set to its title.
                    dbControl.Title = DBTitle;
                    Guid tagID = Guid.NewGuid();
                    dbControl.Tag = tagID.ToString();
                    
                    // list to store the attributes of the control.
                    Dictionary<string, string> list = new Dictionary<string, string>();
                    list.Add(DBAttributeName, DBURLFirstPart +
                        typeValue + DBURLSecPart + Convert.ToString(rangeVal.Text.Trim()));
                    list.Add(DBAttributeLabel, typeValue);
                    ControlProperties customProperties = new ControlProperties(ThisAddIn.DB_NS_URI,
                                                                             tagID.ToString(), list);
                    
                    // add the new control id and its control properties to the collection.
                    ControlsStorage.ControlCollection.Add(tagID, customProperties);
                    
                    // to highlight the newly created markup.
                    BioLitRibbon br = (BioLitRibbon)Globals.Ribbons[0];
                    if (br.tbHighlight.Checked)
                    {
                        rangeVal.HighlightColorIndex = Microsoft.Office.Interop.Word.WdColorIndex.wdYellow;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(Properties.Resources.CUSTOM_MARKUP_ERROR, ex);
            }
        }

        // Function name   : AddMarkupToAll
        // Description     : 
        // Return type     : static void 
        // Argument        : string SrchTxt
        // Argument        : string ID
        // Argument        : string OntologyName
        
        public static void AddMarkupToAll(string SrchTxt, string ID, string OntologyName)
        {
            object missing = System.Type.Missing;
            Word.Range DocRng = Globals.ThisAddIn.Application.ActiveDocument.Content;
            DocRng.Find.ClearFormatting();
            DocRng.Find.Forward = true;
            DocRng.Find.Text = SrchTxt;

            DocRng.Find.Execute(ref missing, ref missing, ref missing, ref missing, ref missing,
                                 ref missing, ref missing, ref missing, ref missing, ref missing,
                                 ref missing, ref missing, ref missing, ref missing, ref missing);

            while (DocRng.Find.Found)
            {
                WordCommonFuncs.OntologyMarkup(DocRng, SmartTagHandlers.GetOntologyID(OntologyName) , ID, OntologyName, OntologyName);
                DocRng.Find.Execute(ref missing, ref missing, ref missing, ref missing, ref missing,
                                    ref missing, ref missing, ref missing, ref missing, ref missing,
                                    ref missing, ref missing, ref missing, ref missing, ref missing);
            }
        }
        
        /// <summary>
        /// Add Markup To All
        /// </summary>
        /// <param name="rangeVal"> word range </param>
        public static void AddMarkupToAll(Word.Range rangeVal)
        {
            object refObject = rangeVal;
            if (rangeVal.ContentControls.Count > 0)
            {
                // controls of biolit namespace are retrieved to mark up all.
                foreach (Word.ContentControl node in rangeVal.ContentControls)
                {
                    Guid controlID = WordCommonFuncs.ConvertToGuid(node.Tag);

                    if (controlID != null && controlID != Guid.Empty && ControlsStorage.ControlCollection.ContainsKey(controlID))
                    {
                        ControlProperties property = ControlsStorage.ControlCollection[controlID];
                        if (property.ControlNamespace == ThisAddIn.BIOLIT_NS_URI)
                        {
                            AddMarkupToAll(rangeVal.Text.Trim(), property.PropertyList["id"], property.PropertyList["OntName"]);
                        }
                    }
                }
            }
        }
    }

        internal class OntologyUtils
        {
            /// <summary>
            /// class name.
            /// </summary>
            private const string ExceptionTitle = "OntologyUtil.cs";

            public static  void AddCustomTag(string Tag, OntologyCache.OntologyNode on)
            {
                try
                {
                    OntologyCache.CustomTags CustTag = new OntologyCache.CustomTags();

                    string FileName = Biolit_Settings.Settings.OBO_DWNLDS + "\\" + "Custom.OBO";
                    bool fFound = false;
                    foreach (OntologyCache.OntologyInfo OntInfo in Globals.ThisAddIn.m_Ontologies)
                    {
                        if (OntInfo.OntologyName == "Custom")
                        {
                            CustTag.AddCustomTerm(Tag, on, FileName, OntInfo, true);
                            fFound = true;
                        }
                    }
                    // for whatever reason if it does not exist create it now 
                    if (fFound == false)
                    {
                        OntologyCache.OntologyInfo CustOnt = new OntologyCache.OntologyInfo();
                        CustOnt.OntologyName = "Custom";
                        Globals.ThisAddIn.m_Ontologies.Add(CustOnt);
                        CustTag.AddCustomTerm(Tag, on, FileName, CustOnt, true);

                    }

                    if (Tag.Contains(' ') || Tag.Contains("_"))
                    {
                        Globals.ThisAddIn.m_BioLitTags.Expressions.Add(new Regex(Tag));

                    }
                    else
                    {
                        Globals.ThisAddIn.m_BioLitTags.Terms.Add(Tag);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message, ExceptionTitle);
                }

                
            }

            public static int GetNumMatches(string Term)
            {
                int Count = 0;
                foreach (OntologyCache.OntologyInfo on in Globals.ThisAddIn.m_Ontologies)
                {
                    if (on.m_TermLookup.ContainsKey(Term))
                    {
                        Count++;
                    }

                }
                return Count;
            }

            public static string[] GetActionCaptions(string Term)
            {
                string[] Captions = null;
                try
                {
                    int Count = GetNumMatches(Term);
                    if (Count > 0)
                    {
                        Captions = new string[Count];
                        int i = 0;
                        foreach (OntologyCache.OntologyInfo OntInfo in Globals.ThisAddIn.m_Ontologies)
                        {
                            if (OntInfo.m_TermLookup.ContainsKey(Term))
                            {
                                OntologyCache.OntologyNode on = OntInfo.m_TermLookup[Term];
                                Captions[i] = "Add mark-up for " + on.id + " - " + on.Definition + "(" + OntInfo.OntologyName + " ontology )";
                                i++;
                                if (i == Count)
                                {
                                    return Captions;
                                }
                            }
                        }

                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message, ExceptionTitle);
                }
                return Captions;
            }

            public static OntologyCache.OntologyNode FindNthMatch(string Term, int N)
            {

                if (N > Globals.ThisAddIn.m_Ontologies.Count)
                {
                    return null;
                }
                int RepCount = 0;
                foreach (OntologyCache.OntologyInfo OntInfo in Globals.ThisAddIn.m_Ontologies)
                {
                    if (OntInfo.m_TermLookup.ContainsKey(Term))
                    {
                        if (RepCount == N)
                        {
                            OntologyCache.OntologyNode on = OntInfo.m_TermLookup[Term];
                            return on;
                        }
                        else
                        {
                            RepCount++;
                        }
                    }
                }

                return null;

            }


            public static OntologyCache.OntologyNode FindNthMatch(string Term)
            {
                foreach (OntologyCache.OntologyInfo OntInfo in Globals.ThisAddIn.m_Ontologies)
                {
                    if (OntInfo.m_TermLookup.ContainsKey(Term))
                    {
                            OntologyCache.OntologyNode on = OntInfo.m_TermLookup[Term];
                            return on;
                    }
                }
                return null;
            }


            public static List<OntologyCache.OntologyNode> FindAllMatches(string Term)
            {
                List<OntologyCache.OntologyNode> nodes = new List<OntologyCache.OntologyNode>();
                foreach (OntologyCache.OntologyInfo OntInfo in Globals.ThisAddIn.m_Ontologies)
                {
                    if (OntInfo.m_TermLookup.ContainsKey(Term))
                    {
                        nodes.Add(OntInfo.m_TermLookup[Term]);
                    }
                }
                return nodes;
            }

        }
    
}
